--[[  

]]

local redis = require "skynet.db.redis"
local cjson = require "cjson"

local timeout = 60

local errors = 
{
    "redis not connected",
    "key is nil",
    "value is nil"    
}

local geounit = 
{
    m = "m",--米
    km = "km",--千米
    mi = "mi",--英里
    ft = "ft",--英尺
}

local georadiusparam=
{
    WITHCOORD = "WITHCOORD",
    WITHDIST = "WITHDIST",
    WITHHASH = "WITHHASH",
}

local geosort =
{
    ASC = "ASC",
    DESC = "DESC",
}


local RedisCmd = {}

local db = nil

--[[
连接redis
]]
function RedisCmd.connect(conf)
    assert(conf,"redis conf is nil")
    db = redis.connect(conf)
end
--[[
断言判断参数
]]

local function check_param(...)
    for i=1,select("#",...) do 
        assert(select(i,...) ~= nil,errors[i])
    end
end



--[[
根据key是否存在value
]]
function RedisCmd.exists(key)
    check_param(db,key)
    return db:exists(key)
end



--[[
 设置key=》value
]]
function RedisCmd.set(key,value)
    check_param(db,key,value)
    return db:set(key,value)
end
--[[
根据key获得value
]]
function RedisCmd.get(key)
    check_param(db,key)
    return db:get(key)
end

--[[
根据多个keys获得value
]]
function RedisCmd.mget(keys)
    check_param(db,keys)
    return db:mget( table.unpack( keys ) )
end

--[[
setex 带生存时间的写入值
]]

function RedisCmd.setex(key,time,value)
     check_param(db,key,value)
     time = time or timeout
     db:setex(key,time,value)
end

--[[
    判断是否重复的，写入值 如果已经写入则不修改
]]
function RedisCmd.setnx(key,value)
     check_param(db,key,value)
     db:setnx(key,value)
end

--[[
删除指定的key
key 可以是单个也可以是一个table 返回被删除的个数
]]

function RedisCmd.delete(key)
    check_param(db,key)
    return db:del(key)
end

--[[
名称为key的集合中查找是否有value元素，有ture 没有 false
]]
function RedisCmd.sismember(key,value)
     check_param(db,key,value)
     return db:sismember(key,value)
end


--[[
向名称为key的set中添加元素value,如果value存在，不写入，return false
]]
function RedisCmd.sAdd(key,value)
    check_param(db,key,value)
    return db:sAdd(key,value)
end

--[[
删除名称为key的set中的元素value
]]
function RedisCmd.sRem(key,value)
    check_param(db,key,value)
    return db:sRem(key,value)
end

--[[
删除名称为key的set中的元素value
]]
function RedisCmd.sMove(seckey, dstkey, value)
    return db:sMove(seckey, dstkey, value)
end

--[[
返回名称为key的set的所有元素
]]
function RedisCmd.sMembers(key)
    check_param(db,key)
    return db:sMembers(key)
end

-------------------------list相关操作-----------------------
--[[
在名称为key的list左边（头）添加一个值为value的 元素
]]
function RedisCmd.lPush(key, value)
    check_param(db,key,value)
    return db:lPush(key,value)
end

--[[
在名称为key的list右边（尾）添加一个值为value的 元素
]]
function RedisCmd.rPush(key, value)
    check_param(db,key,value)
    return db:rPush(key,value)
end


--[[
在名称为key的list左边（头）添加一个值为value的 元素
]]
function RedisCmd.lPushx(key, value)
    check_param(db,key,value)
    return db:lPushx(key,value)
end

--[[
在名称为key的list右边（尾）添加一个值为value的 元素
]]
function RedisCmd.rPushx(key, value)
    check_param(db,key,value)
    return db:rPushx(key,value)
end


--[[
输出名称为key的list左(头)起/右（尾）起的第一个元素，删除该元素
]]
function RedisCmd.lPop(key)
    check_param(db,key)
    return db:lPop(key)
end

--[[
输出名称为key的list左(头)起/右（尾）起的第一个元素，删除该元素
]]
function RedisCmd.rPop(key)
    check_param(db,key)
    return db:rPop(key)
end


-- --[[
-- 返回名称为key的list有多少个元素
-- ]]
-- function RedisCmd.lSize(key)
--     check_param(db,key)
--     return db:lsize(key)
-- end

--[[
返回名称为key的list中index位置的元素
]]
function RedisCmd.lIndex(key,index)
     check_param(db,key)
     return db:lIndex(key,index)
end


--[[
返回名称为key的list中index位置的元素
]]
function RedisCmd.lSet(key,index,value)
    check_param(db,key,value)
    db:lSet(key,index,value)
end

--[[
返回名称为key的list中start至end之间的元素（end为 -1 ，返回所有）
]]
function RedisCmd.lRange(key,startIndex,endIndex)
    check_param(db,key)
    return db:lRange(key,startIndex,endIndex)
end

--[[
返回key所对应的list元素个数
]]
function RedisCmd.lLen(key)
    check_param(db,key)
    return db:lLen(key)
end


--[[
截取名称为key的list，保留start至end之间的元素
]]
function RedisCmd.lTrim(key,startIndex,endIndex)
    check_param(db,key)
    return db:lTrim(key,startIndex,endIndex)
end


--[[
删除count个名称为key的list中值为value的元素。count为0，删除所有值为value的元素，count>0从头至尾删除count个值为value的元素，count<0从尾到头删除|count|个值为value的元素
]]
function RedisCmd.lRem(key,value,count)
    check_param(db,key,value)
    db:lrem(key,count,value)
end


--[[
在名称为为key的list中，找到值为pivot 的value，并根据参数Redis.:BEFORE | Redis.:AFTER，来确定，newvalue 是放在 pivot 的前面，或者后面。如果key不存在，不会插入，如果 pivot不存在，return -1

]]
function RedisCmd.lInsert(key,insertMode,value,newValue)
    check_param(db,key)
    insertMode = insertMode or "after"
    db:lInsert(key,insertMode,value,newValue)
end

-->>>>>>>>>>>>>>hash操作>>>>>>>>>>>>>>>>

--[[
向名称为h的hash中添加元素key—>value
]]
function RedisCmd.hSet(h,key,value)
    check_param(db,key,value)
    return db:hset(h,key,value)
end
--[[
返回名称为h的hash中key对应的value
]]
function RedisCmd.hGet(h,key)
    check_param(db,key)
    return db:hget(h,key)
end
--[[
返回名称为h的hash中元素个数
]]
function RedisCmd.hLen(h)
    return db:hlen(h)
end
--[[
删除名称为h的hash中键为key的域
]]
function RedisCmd.hDel(h,key)
    check_param(db,key)
    return db:hDel(h,key)
end
--[[
  返回名称为key的hash中所有键
]]
function RedisCmd.hKeys(h)
    return db:hKeys(h)
end
--[[
返回名称为h的hash中所有键对应的value
]]
function RedisCmd.hVals(h)
   return db:hVals(h)
end

--[[
返回名称为h的hash中所有的键（key）及其对应的value
]]
function RedisCmd.hGetAll(h)
   return db:hGetAll(h)
end


--[[
名称为h的hash中是否存在键名字为key的域
]]
function RedisCmd.hExists(h,key)
   check_param(db,key)
   return db:hExists(h,key)
end


--[[
将名称为h的hash中key的value增加number
]]
function RedisCmd.hIncrBy(h,key,number)
   check_param(db,key)
   assert(type(number) == "number")
   return db:hIncrBy(h,key,number)
end

--[[
向名称为key的hash中批量添加元素
]]
function RedisCmd.hMset(h,Table)
    db:hMset(h,table.unpack(Table));
end
--[[
返回名称为h的hash中keytable中key对应的value
]]
function RedisCmd.hMGet(h,keyTable)
    return db:hMGet(h,table.unpack(keyTable))
end

------------------------zset begin--------------------------
--添加一个到有序集合,或者如果它已经存在更新其分数
function RedisCmd.zAdd(key,score,member)
    check_param(db,key)
    return db:zadd(key,score,member)
end

function RedisCmd.zNAdd(key, ...)
    check_param(db,key)
    return db:zadd(key, ...)
end

--得到的有序集合成员的数量
function RedisCmd.zCard(key)
    check_param(db,key)
    return db:zcard(key)
end

--计算一个有序集合成员与给定值范围内的分数
function RedisCmd.zCount(key,min,max)
    check_param(db,key)
    return db:zcount(key,min,max)
end

--获取给定成员相关联的分数在一个有序集合
function RedisCmd.zScore(key,member)
    check_param(db,key)
    return db:zscore(key,member)
end

--确定一个有序集合成员的索引，以分数排序，从高分到低分
function RedisCmd.zRevRank(key,member)
    check_param(db,key)
    return db:zrevrank(key,member)
end

--确定成员的索引中有序集合
function RedisCmd.zRank(key,member)
    check_param(db,key)
    return db:zrank(key,member)
end

--从有序集合中删除一个
function RedisCmd.zRem(key,member)
    check_param(db,key)
    return db:zrem(key,member)
end

--删除所有成员在给定的字典范围之间的有序集合
function RedisCmd.zRemRangeByLex(key,min,max)
    check_param(db,key)
    return db:zremrangebylex(key,min,max)
end

--由索引返回一个成员范围的有序集合。
function RedisCmd.zRange(key,start,stop,isWithscores)
    check_param(db,key)
    if isWithscores then
        return db:zrange(key,start,stop,"WITHSCORES")
    end
    return db:zrange(key,start,stop)
end

--返回一个成员范围的有序集合，通过索引，以分数排序，从高分到低分
function RedisCmd.zRevRange(key,start,stop,isWithscores)
    check_param(db,key)
    if isWithscores then
        return db:zrevrange(key,start,stop,"WITHSCORES")
    end
    return db:zrevrange(key,start,stop)
end

--在给定的索引之内删除所有成员的有序集合
function RedisCmd.zRemRangeByRank(key,start,stop)
    check_param(db,key)
    return db:zremrangebyrank(key,start,stop)
end

--在给定的分数之内删除所有成员的有序集合
function RedisCmd.zRemRangeByScore(key,start,stop)
    check_param(db,key)
    return db:zremrangebyscore(key,start,stop)
end

--按分数返回一个成员范围的有序集合。
function RedisCmd.zRangeByScore(key,min,max,isWithscores)
    check_param(db,key)
    if isWithscores then
        return db:zrangebyscore(key,min,max,"WITHSCORES")
    end
    return db:zrangebyscore(key,min,max)
end

--返回一个成员范围的有序集合，按分数，以分数排序从高分到低分
function RedisCmd.zRevRangeByScore(key,max,min,isWithscores)
    check_param(db,key)
    if isWithscores then
        return db:zrevrangebyscore(key,max,min,"WITHSCORES")
    end
    return db:zrevrangebyscore(key,max,min)
end

--计算一个给定的字典范围之间的有序集合成员的数量
function RedisCmd.zLexCount(key,min,max)
    check_param(db,key)
    return db:zlexcount(key,min,max)
end

--返回一个成员范围的有序集合（由字典范围）
function RedisCmd.zRangeByLex(key,min,max)
    check_param(db,key)
    return db:zrangebylex(key,min,max)
end

----------------------db其它操作-----------------------------
--[[
清空当前数据库
]]
function RedisCmd.flushDB()
    db:flushDB()
end
--[[
清空所有数据库
]]
function RedisCmd.flushAll()
    db:flushAll()
end

--[[
给key重命名
]]
function RedisCmd.rename(key,newKey)
    check_param(db,key)
    if RedisCmd.exists(key) then
        db:renameNx(key,newKey)
    end
end

--[[
设定一个key的活动时间（s）
]]
function RedisCmd.setTimeout(key,time)
    check_param(db,key)
    if RedisCmd.exists(key) then 
        db:setTimeout(key,time)
    end
end

--[[
key存活到一个unix时间戳时间
]]
function RedisCmd.expireAt(key,time)
    check_param(db,key)
     if RedisCmd.exists(key) then
        db:expireAt(key,time)
     end
end

--[[
返回满足给定pattern的所有key
]]
function RedisCmd.keys(key)
    check_param(db,key)
    return db:keys(key)
end

function RedisCmd.dbSize()
    db:dbsize()
end

--[[ geo hash 相关操作 
有效的经度介于 -180 度至 180 度之间。
有效的纬度介于 -85.05112878 度至 85.05112878 度之间
]]
function RedisCmd.geoadd(key, longitude, latitude, member)
    check_param(db,key)
    return db:geoadd(key, longitude, latitude, member)
end

-- 获取一个位置
function RedisCmd.geopos(key, member)
    check_param(db,key)
    return db:geopos(key, member)
end

-- 获取多个位置
function RedisCmd.geoposs(key, members)
    check_param(db,key)
    if members and next(members) then
        return db:geopos(key, table.unpack(members))
    else
        print("invalide geoposs of members")
    end
end

--[[
GEODIST key member1 member2 [unit]

返回两个给定位置之间的距离。

如果两个位置之间的其中一个不存在， 那么命令返回空值。

指定单位的参数 unit 必须是以下单位的其中一个：

m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
如果用户没有显式地指定单位参数， 那么 GEODIST 默认使用米作为单位。
]]
function RedisCmd.geodist(key, member1, member2, unit)
    check_param(db,key)
    if not unit then unit = "m" end
    if member1 and member2 and geounit[unit] then
        return db:geodist(key, member1, member2, geounit[unit])
    else
        print("invalide geodist of members")
    end
end

--[[
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

以给定的经纬度为中心， 返回键包含的位置元素当中， 与中心的距离不超过给定最大距离的所有位置元素。

范围可以使用以下其中一个单位：

m 表示单位为米。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
在给定以下可选项时， 命令会返回额外的信息：

WITHDIST ： 在返回位置元素的同时， 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD ： 将位置元素的经度和维度也一并返回。
WITHHASH ： 以 52 位有符号整数的形式， 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试， 实际中的作用并不大。
命令默认返回未排序的位置元素。 通过以下两个参数， 用户可以指定被返回位置元素的排序方式：

ASC ： 根据中心的位置， 按照从近到远的方式返回位置元素。
DESC ： 根据中心的位置， 按照从远到近的方式返回位置元素。
在默认情况下， GEORADIUS 命令会返回所有匹配的位置元素。 虽然用户可以使用 COUNT <count> 选项去获取前 N 个匹配元素， 但是因为命令在内部可能会需要对所有被匹配的元素进行处理， 所以在对一个非常大的区域进行搜索时， 即使只使用 COUNT 选项去获取少量元素， 命令的执行速度也可能会非常慢。 但是从另一方面来说， 使用 COUNT 选项去减少需要返回的元素数量， 对于减少带宽来说仍然是非常有用的。
]]
function RedisCmd.georadius(key, longitude, latitude, radius, unit, otherparm, sort, count)
    check_param(db,key)
    if not unit then unit = "km" end
    if longitude and latitude and geounit[unit] then
        local parms = {} -- georadiusparam
        if otherparm and georadiusparam[otherparm] then
            table.insert(parms,georadiusparam[otherparm])
        end
        if sort and geosort[sort] then
            table.insert(parms,geosort[sort])
        end
        if count then
            table.insert(parms,"COUNT")
            table.insert(parms,count)
        end
        return db:georadius(key, longitude, latitude, radius, geounit[unit],table.unpack(parms))
    else
        print("invalide georadius of members")
    end
end

--[[
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]

这个命令和 GEORADIUS 命令一样， 都可以找出位于指定范围内的元素， 但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的， 而不是像 GEORADIUS 那样， 使用输入的经度和纬度来决定中心点。

关于 GEORADIUSBYMEMBER 命令的更多信息， 请参考 GEORADIUS 命令的文档。
]]
function RedisCmd.georadiusbymember(key, member, radius, unit, otherparm, sort, count)
    check_param(db,key)
    if not unit then unit = "km" end
    if member and geounit[unit] then
        local parms = {} -- georadiusparam
        if otherparm and georadiusparam[otherparm] then
            table.insert(parms,georadiusparam[otherparm])
        end
        if sort and geosort[sort] then
            table.insert(parms,geosort[sort])
        end
        if count then
            table.insert(parms,"COUNT")
            table.insert(parms,count)
        end
        return db:georadiusbymember(key, member, radius, geounit[unit],table.unpack(parms))
    else
        print("invalide geodist of members")
    end
end

-- 获取一个hash值
function RedisCmd.geohash(key, member)
    check_param(db,key)
    return db:geohash(key, member)
end

-- 获取多个hash值
function RedisCmd.geohashs(key, members)
    check_param(db,key)
    if members and next(members) then
        return db:geopos(key, table.unpack(members))
    else
        print("invalide geohashs of members")
    end
end

--[[
自减一
]]
function RedisCmd.decr(key)
    check_param(db,key)
    return db:decr(key)
end

function RedisCmd.decrby(key,decrement)
    check_param(db,key,decrement)
    return db:decrby(key,decrement)
end

function RedisCmd.incr(key)
    check_param(db,key)
    return db:incr(key)
end

function RedisCmd.incrby(key,increment)
    check_param(db,key,increment)
    return db:incrby(key,increment)
end

--[[
销毁实例
]]
function RedisCmd.disconnect()
    if db then
        db:disconnect()
        db = nil
    end
end

return RedisCmd
